﻿/// <summary>
/// Create By:  Alex Sanchez Sanchez
/// Date:       19/11/2012
/// Time:       13:10
/// Update:     
/// Time Update: 
/// Fix Bugs Descripion:
/// 
/// </summary>

using System;
using System.Collections;
using System.Data;
using Npgsql;

namespace LayerData.DataBaseManager
{
    /// <summary>
    /// Proporciona metodos basicos para el manejo de SP.
    /// Esta clase no se puede heredar.
    /// </summary>
    internal sealed class PostgreSqlHelperParameterCache
    {
        #region Variables
        private static readonly Hashtable paramCache = Hashtable.Synchronized(new Hashtable());
        #endregion

        #region Constructores
        private PostgreSqlHelperParameterCache()
        {
        }

        #endregion

        #region Funciones Privadas

        /// <summary>
        /// resolver en tiempo de ejecución el conjunto adecuado de SqlParameters para un procedimiento almacenado
        /// </summary>
        /// <param name="connectionInfo">Informacion de la Conexion al Servidor de Datos</param>
        /// <param name="strSpName">Nombre del Procedimiento Almacenado, debe ser valido y existir en la base de datos.</param>
        /// <param name="includeReturnValueParameter">Indica si se incluira el Parametro @RetunrValue</param>
        /// <returns>
        /// Un <see cref="NpgsqlParameter[]"/> con los parametros del SP Especificado, si se proporciono un nombre invalido
        /// o si ocurrio un error, retorna null.
        /// </returns>
        private static NpgsqlParameter[] DiscoverSpParameterSet(IPostgreSqlConnectionInfo connectionInfo, string strSpName, bool includeReturnValueParameter)
        {
            NpgsqlParameter[] parameters = null;
            NpgsqlConnection con = connectionInfo.GetPostgreSqlConnection();
            try
            {
                connectionInfo.OpenConnection(con);
                NpgsqlCommand cmd = new NpgsqlCommand(strSpName, con);
                cmd.CommandType = CommandType.StoredProcedure;
                NpgsqlCommandBuilder.DeriveParameters(cmd);

                //quitamos esto 
                //if (!includeReturnValueParameter)
                //{
                //    cmd.Parameters.RemoveAt(0);
                //}
                parameters = new NpgsqlParameter[cmd.Parameters.Count];
                cmd.Parameters.CopyTo(parameters, 0);
            }
            catch (NpgsqlException ex)
            {
                parameters = null;
                Console.Write(string.Format("Error al Recuperar los Parametros, revise lo Siguiente: \n{0}", ex.Message));
            }
            finally
            {
                connectionInfo.CloseConnection(con);
            }

            return parameters;
        }
        
        /// <summary>
        /// Genera una Copia de una Coleccion de parametros.
        /// </summary>
        /// <param name="originalParameters">Colecion de parametros a clonar.</param>
        /// <returns></returns>
        private static NpgsqlParameter[] CloneParameters(NpgsqlParameter[] originalParameters)
        {
            NpgsqlParameter[] clonedParameters = new NpgsqlParameter[originalParameters.Length];

            for (int i = 0, j = originalParameters.Length; i < j; i++)
            {
                clonedParameters[i] = (NpgsqlParameter)((ICloneable)originalParameters[i]).Clone();
            }

            return clonedParameters;
        }

        #endregion

        #region Funciones Publicas
        #region Funciones Cache

        /// <summary>
        /// Agrega el parametro a la memoria cache
        /// </summary>
        /// <param name="connectionString"><see cref="IPostgreSqlConnectionInfo"/> con todos los parametros necesarios para la conexion</param>
        /// <param name="commandText">El nombre del procedimiento almacenado o comando T-SQL.</param>
        /// <param name="commandParameters">Colecion de parametros a agregar a la memoria cache.</param>
        public static void CacheParameterSet(IPostgreSqlConnectionInfo connectionInfo, string commandText, params NpgsqlParameter[] commandParameters)
        {
            string hashKey = string.Format("{0}:{1}", connectionInfo.GetStringPostgreSqlConnection(), commandText);

            paramCache[hashKey] = commandParameters;
        }

        /// <summary>
        /// recuperar un Array de parámetros de la memoria caché
        /// </summary>
        /// <param name="connectionString"><see cref="IPostgreSqlConnectionInfo"/> con todos los parametros necesarios para la conexion</param>
        /// <param name="commandText">El nombre del procedimiento almacenado o comando T-SQL.</param>
        /// <returns>Una colecion de NpgsqlParameter</returns>
        public static NpgsqlParameter[] GetCachedParameterSet(IPostgreSqlConnectionInfo connectionInfo, string commandText)
        {
            string hashKey = string.Format("{0}:{1}", connectionInfo.GetStringPostgreSqlConnection(), commandText);

            NpgsqlParameter[] cachedParameters = (NpgsqlParameter[])paramCache[hashKey];

            if (cachedParameters == null)
            {
                return null;
            }
            else
            {
                return CloneParameters(cachedParameters);
            }
        }

        #endregion

        #region Funciones de Recuepracion de parametros
        /// <summary>
        /// Recupera el conjunto de NpgsqlParameter adecuados para el procedimiento almacenado
        /// </summary>
        /// <remarks>
        /// Este método consulta la base de datos para obtener la información, y luego guardarlo en una memoria caché para futuras peticiones.
        /// </remarks>
        /// <param name="connectionString"><see cref="IPostgreSqlConnectionInfo"/> con todos los parametros necesarios para la conexion</param>
        /// <param name="spName">El nombre del procedimiento almacenado o comando T-SQL.</param>
        /// <returns>Una colecion de NpgsqlParameter</returns>
        public static NpgsqlParameter[] GetSpParameterSet(IPostgreSqlConnectionInfo connectionInfo, string spName)
        {
            return GetSpParameterSet(connectionInfo, spName, false);
        }

        /// <summary>
        /// Recupera el conjunto de NpgsqlParameter adecuados para el procedimiento almacenado
        /// </summary>
        /// <remarks>
        /// Este método consulta la base de datos para obtener la información, y luego guardarlo en una memoria caché para futuras peticiones.
        /// </remarks>
        /// <param name="connectionString"><see cref="IPostgreSqlConnectionInfo"/> con todos los parametros necesarios para la conexion</param>
        /// <param name="spName">El nombre del procedimiento almacenado o comando T-SQL.</param>
        /// <param name="includeReturnValueParameter">un valor bool que indica si el parámetro de valor de retorno debe ser incluido en los resultados</param>
        /// <returns>Una colecion de NpgsqlParameter</returns>
        public static NpgsqlParameter[] GetSpParameterSet(IPostgreSqlConnectionInfo connectionInfo, string spName, bool includeReturnValueParameter)
        {
            string hashKey = string.Format("{0}:{1}{2}", connectionInfo.GetStringPostgreSqlConnection(), spName, includeReturnValueParameter ? ":include ReturnValue Parameter" : "");

            NpgsqlParameter[] cachedParameters;

            cachedParameters = (NpgsqlParameter[])paramCache[hashKey];

            if (cachedParameters == null)
            {
                cachedParameters = (NpgsqlParameter[])(paramCache[hashKey] = DiscoverSpParameterSet(connectionInfo, spName, includeReturnValueParameter));
            }

            return CloneParameters(cachedParameters);
        }
        #endregion Parameter Discovery Functions
        #endregion
    }
}